#ifndef SCENE_H_
#define SCENE_H_
#include <GL/glew.h>
#include <GL/glut.h>
#include "../common/defs.h"
#include "../common/vconsole.h"

class scene
{
private:
    
    static void reshape_callback(int w, int h)
    {
        glViewport(0, 0, (GLsizei)w, (GLsizei)h);
        glMatrixMode (GL_MODELVIEW);
        glLoadIdentity();
        glMatrixMode (GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
    }

    
public:
    scene()
    {
    }
    
    ~scene()
    {
    }

    bool init_graphics()
    {
        if (glfwInit() != GL_TRUE)
        {
            vconsole::print_status("glfw init", SCENE, false);
            return false;
        }
        vconsole::print_status("glfw init", SCENE, true);
        
        if (glfwOpenWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 5, 6, 5, 0, 0, 0, GLFW_WINDOW) != GL_TRUE)
        {
            vconsole::print_status("opening window", SCENE, false);
            return false;
        }
        vconsole::print_status("opening window", SCENE, true);
        
        if (glewInit() != GLEW_OK)
        {
            vconsole::print_status("glew init", SCENE, false);
            return false;
        }
        vconsole::print_status("glew init", SCENE, true);

        glfwSetWindowTitle(WINDOW_TITLE);
        //glfwDisable(GLFW_MOUSE_CURSOR);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        glShadeModel(GL_SMOOTH);

        // set handlers
        glfwSetWindowSizeCallback(reshape_callback);
    
        glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
        glDisable(GL_DEPTH_TEST);
        // set view matrix
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);

        return true;
    }
    
    void create_buffer(GLuint *pixel_buffer, GLuint *result_texture)
    {
        // Generate a buffer ID called a PBO (Pixel Buffer Object)
        glGenBuffers(1, pixel_buffer);
        // Make this the current UNPACK buffer (OpenGL is state-based)
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, *pixel_buffer);
        // Allocate data for the buffer. 4-channel 8-bit image
        glBufferData(GL_PIXEL_UNPACK_BUFFER, WINDOW_WIDTH * WINDOW_HEIGHT * 4 * sizeof(GLubyte), NULL, GL_DYNAMIC_COPY);
        //cudaGLRegisterBufferObject(*__pixel_buffer);
               
        
        // Enable Texturing
        glEnable(GL_TEXTURE_2D);

        // Generate a texture identifier
        glGenTextures(1, result_texture);

        // Make this the current texture (remember that GL is state-based)
        glBindTexture( GL_TEXTURE_2D, *result_texture);

        // Allocate the texture memory. The last parameter is NULL since we only
        // want to allocate memory, not initialize it
        glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, WINDOW_WIDTH, WINDOW_HEIGHT, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);

        // Must set the filter mode, GL_LINEAR enables interpolation when scaling
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    }
    
    void draw_scene(const GLuint *pixel_buffer, const GLuint *result_texture)
    {                
        // Create a texture from the buffer
        glBindBuffer( GL_PIXEL_UNPACK_BUFFER, *pixel_buffer);

        // bind texture from PBO
        glBindTexture(GL_TEXTURE_2D, *result_texture);


        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, NULL);


        // Draw a single Quad with texture coordinates for each vertex.
        glBegin(GL_QUADS);
            glTexCoord2f(0.0f,1.0f); glVertex3f(0.0f,0.0f,0.0f);
            glTexCoord2f(0.0f,0.0f); glVertex3f(0.0f,1.0f,0.0f);
            glTexCoord2f(1.0f,0.0f); glVertex3f(1.0f,1.0f,0.0f);
            glTexCoord2f(1.0f,1.0f); glVertex3f(1.0f,0.0f,0.0f);
        glEnd();

        glfwSwapBuffers();
    }
};

#endif
